from spargameproject.spargame.game.engine.game_engine import PlayHandler
from spargameproject.spargame.game.playhandlers.drop_validations import checkForGbaa
from spargameproject.spargame.game.playhandlers.spar_play_handlers import \
  getActiveGamePlayersWithoutObserversAndLosers, determineTurnAndLeadPlayer, updateWinStack, \
  updateGameOverWinnerAndPoints
from spargameproject.spargame.models import GamePlayer, PlayMetaData, Play
from spargameproject.util import inPlaceUnique

class DropHandler(PlayHandler):
  def getType(self):
    return 'drop'

  def handlePlay(self, play, game_engine):
    session = play.game.getMetadata('session')
    play.setMetadata('session', session)

    card_indeces_str = play.getMetadata('card_indeces')
    if not card_indeces_str:
      return ['Please select one or more cards'], {}
    card_indeces = [int(x) for x in card_indeces_str.split(",")]
    play.setMetadata('card_indeces', card_indeces)
    response = {}
    errors = []
    game_player = GamePlayer.objects.get(game=play.game, player=play.player)

    sub_type = play.getMetadata('play_sub_type') or 'normal'

    if play.game.getMetadata('turn_player') != play.player.id:
      return ['Cannot make play since it\'s not your turn'], response
    if game_player.getMetadata('lost'):
      return ['Cannot make play since you\'ve already lost this round'], response

    cards_left = game_player.getMetadata('cards_left')
    current_cards_left = game_player.getMetadata('current_cards_left')
    hand = game_player.getMetadata('hand')

    card_indeces = inPlaceUnique(card_indeces)

    card_to_beat = play.game.getMetadata('card_to_beat')
    previous_lead_player = play.game.getMetadata('lead_player')
    dry_card_indeces = game_player.getMetadata('dry_card_indeces')
    dry_cards = [hand[session][dry_card_index] for dry_card_index in dry_card_indeces]

    is_gbaa = checkForGbaa(card_to_beat, card_indeces, hand[session], cards_left, dry_cards,
                           previous_lead_player, play.player.id, play.getMetadata('is_standby'))

    if is_gbaa:
      return [is_gbaa], response

    # Put every card except first on standby.
    if len(card_indeces) > 1:
      game_player.setMetadata('standby_cards', card_indeces[1:])

    # Extract the indeces of the cards played.
    total_cards_left = len(current_cards_left) + len(dry_card_indeces)
    first_card_number = 5 - (total_cards_left)
    last_card_number = first_card_number + len(card_indeces)
    card_number_array = [x for x in range(first_card_number, last_card_number)]
    card_numbers = ','.join([str(x) for x in card_number_array])
    response['card_numbers'] = card_numbers
    play.setMetadata('card_numbers', card_number_array)

    if sub_type == "dry":
      dry_card_indeces = [x for x in dry_card_indeces if not x in card_indeces]
      game_player.setMetadata('dry_card_indeces', dry_card_indeces)

    # Build the display message and update cards_left.
    message = 'Played a %s card' % sub_type
    for card_index in card_indeces:
      message = message + ' card_image_' + hand[session][card_index].fileFormat()
      cards_left[card_index] = None
    play.setMetadata('message', message)
    response['message'] = message

    # update the current_cards_left metadata
    current_cards_left = [card for card in cards_left if card]
    game_player.setMetadata('cards_left', cards_left)
    game_player.setMetadata('current_cards_left', current_cards_left)

    # Process the play and determine the new lead and turn players.
    game = game_player.game
    active_game_players = getActiveGamePlayersWithoutObserversAndLosers(game)
    player_index = active_game_players.index(GamePlayer.objects.get(game=game, player=play.player))
    first_card_played = hand[session][card_indeces[0]]
    next_index, new_lead_player, card_to_beat = determineTurnAndLeadPlayer(
        active_game_players, player_index, card_to_beat, first_card_played, previous_lead_player, game)
    game.setMetadataWithMap({'turn_player': active_game_players[next_index].player.id,
                             'lead_player': new_lead_player})
    new_lead_game_player = GamePlayer.objects.get(player=new_lead_player, game=game)
    updateWinStack(game_player, new_lead_game_player, card_to_beat, sub_type)

    # Increment the number played every time there is a play
    players_played = play.game.getMetadata('players_played') or set()
    players_played.add(game_player.player.id)
    if len(players_played) >= len(active_game_players):
      players_played = set()
    game.setMetadata('players_played', players_played)
    updateGameOverWinnerAndPoints(game, new_lead_player, active_game_players, players_played,
                                       session)

    game.setMetadata('next_to_last_player', game.getMetadata('last_player'))
    game.setMetadata('last_player', game_player.player.id)

    # Process standby cards for next player if applicable
    standby_cards = active_game_players[next_index].getMetadata('standby_cards')
    if standby_cards:
      standby_play = Play.objects.create(player=active_game_players[next_index].player,
                                         game=active_game_players[next_index].game)
      standby_play.setMetadata('is_standby', True)
      PlayMetaData.objects.create(
          play=standby_play, data_key='card_indeces', data_value=str(standby_cards[0]))
      active_game_players[next_index].setMetadata('standby_cards', standby_cards[1:])
      standby_errors, standby_response = self.handlePlay(standby_play, game_engine)
      if standby_errors:
        active_game_players[next_index].setMetadata('lost', True)
        losers = game.getMetadata('losers') or []
        losers.append(active_game_players[next_index].player.id)
        game.setMetadata('losers', losers)
        players_played = play.game.getMetadata('players_played') or set()
        current_player = active_game_players[next_index].player.id
        if current_player in players_played:
          players_played.remove(current_player)
        game.setMetadata('players_played', players_played)

    return errors, response
